package org.ehe.finance.voucher.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ehe.common.core.exception.ServiceException;
import org.ehe.common.core.utils.StringUtils;
import org.ehe.common.mybatis.core.page.PageQuery;
import org.ehe.common.mybatis.core.page.TableDataInfo;
import org.ehe.common.satoken.utils.LoginHelper;
import org.ehe.finance.voucher.domain.ErpFinanceVoucher;
import org.ehe.finance.voucher.domain.ErpFinanceVoucherType;
import org.ehe.finance.voucher.domain.bo.VoucherTypeCreateBo;
import org.ehe.finance.voucher.domain.bo.VoucherTypeQueryBo;
import org.ehe.finance.voucher.domain.bo.VoucherTypeUpdateBo;
import org.ehe.finance.voucher.domain.vo.ErpFinanceVoucherTypeVo;
import org.ehe.finance.voucher.domain.vo.ErpFinanceVoucherVo;
import org.ehe.finance.voucher.mapper.ErpFinanceVoucherMapper;
import org.ehe.finance.voucher.mapper.ErpFinanceVoucherTypeMapper;
import org.ehe.finance.voucher.service.ErpFinanceVoucherTypeService;
import org.springframework.stereotype.Service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author :zhangnn
 * @className :ErpFinanceVoucherTypeServiceImpl
 * @description: TODO
 * @date 2025-08-22 17:47:26
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ErpFinanceVoucherTypeServiceImpl extends ServiceImpl<ErpFinanceVoucherTypeMapper, ErpFinanceVoucherType>
    implements ErpFinanceVoucherTypeService {

    private final ErpFinanceVoucherTypeMapper voucherTypeMapper;
    private final ErpFinanceVoucherMapper voucherMapper;

    /**
     * 分页
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<ErpFinanceVoucherTypeVo> pageVoucherTypes(VoucherTypeQueryBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<ErpFinanceVoucherType> lqw = new LambdaQueryWrapper<>();
        lqw.like(StringUtils.isNotBlank(bo.getTypeCode()),ErpFinanceVoucherType::getTypeCode,bo.getTypeCode());
        lqw.like(StringUtils.isNotBlank(bo.getTypeName()),ErpFinanceVoucherType::getTypeName,bo.getTypeName());
        lqw.eq(null != bo.getStatus(),ErpFinanceVoucherType::getStatus,bo.getStatus());

        IPage<ErpFinanceVoucherTypeVo> result = voucherTypeMapper.selectVoPage(pageQuery.build(),lqw);

        return TableDataInfo.build(result);
    }

    /**
     * 新增
     * @param createDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createVoucherType(VoucherTypeCreateBo createDTO) {
        // 检查编码是否存在
        if (checkTypeCodeExists(createDTO.getTypeCode(), null)) {
            throw new ServiceException("凭证类别编码已存在");
        }

        // 检查凭证字是否存在
        if (checkPrefixExists(createDTO.getPrefix(), null)) {
            throw new ServiceException("凭证字已存在");
        }

        ErpFinanceVoucherType entity = new ErpFinanceVoucherType();
        BeanUtil.copyProperties(createDTO, entity);
        entity.setTenantId(LoginHelper.getTenantId());
        entity.setCreateDept(LoginHelper.getDeptId());

        if (save(entity)) {
            log.info("创建凭证类别成功，ID: {}", entity.getId());
            return entity.getId();
        }

        throw new ServiceException("创建凭证类别失败");
    }

    /**
     * 修改
     * @param updateDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateVoucherType(VoucherTypeUpdateBo updateDTO) {
        ErpFinanceVoucherType existEntity = getById(updateDTO.getId());
        if (existEntity == null) {
            throw new ServiceException("凭证类别不存在");
        }

        // 检查凭证字是否存在（排除自己）
        if (!existEntity.getPrefix().equals(updateDTO.getPrefix())) {
            //不做任何操作
             if (checkPrefixExists(updateDTO.getPrefix(), null)) {
                 throw new ServiceException("凭证字已存在");
             }
        }

        BeanUtil.copyProperties(updateDTO, existEntity, "id", "typeCode");

        if (!updateById(existEntity)) {
            throw new ServiceException("更新凭证类别失败");
        }

        log.info("更新凭证类别成功，ID: {}", updateDTO.getId());
    }

    /**
     * 删除
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteVoucherType(Long id) {
        ErpFinanceVoucherType entity = getById(id);
        if (entity == null) {
            throw new ServiceException("凭证类别不存在");
        }
        ErpFinanceVoucherVo erpFinanceVoucherVo = voucherMapper.selectVoOne(new LambdaQueryWrapper<ErpFinanceVoucher>()
            .eq(ErpFinanceVoucher::getVoucherTypeId, id));
        if(Objects.nonNull(erpFinanceVoucherVo)){
            throw new ServiceException("票证类别已被使用，无法进行删除！");
        }

        if (!removeById(id)) {
            throw new ServiceException("删除凭证类别失败");
        }

        log.info("删除凭证类别成功，ID: {}", id);
    }




    /**
     * 批量删除
     * @param ids
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDeleteVoucherTypes(List<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return;
        }

        for (Long id : ids) {
            deleteVoucherType(id);
        }
    }

    @Override
    public ErpFinanceVoucherTypeVo getVoucherTypeById(Long id) {
        ErpFinanceVoucherType entity = getById(id);
        if (entity == null) {
            throw new ServiceException("凭证类别不存在");
        }

        return convertToVO(entity);
    }

    /**
     * 获取启用的凭证类别列表
     * @return
     */
    @Override
    public List<ErpFinanceVoucherTypeVo> getEnabledVoucherTypes() {
        List<ErpFinanceVoucherType> list = this.baseMapper.selectList(new LambdaQueryWrapper<ErpFinanceVoucherType>()
            .eq(ErpFinanceVoucherType::getStatus,1));
        return list.stream().map(this::convertToVO).collect(Collectors.toList());
    }

    /**
     * 修改状态
     * @param id
     * @param status
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeStatus(Long id, Integer status) {
        ErpFinanceVoucherType entity = getById(id);
        if (entity == null) {
            throw new ServiceException("凭证类别不存在");
        }

        entity.setStatus(status);
        if (!updateById(entity)) {
            throw new ServiceException("修改状态失败");
        }

        log.info("修改凭证类别状态成功，ID: {}, Status: {}", id, status);
    }

    /**
     * 校验类型code是否存在
     * @param typeCode
     * @param excludeId
     * @return
     */
    @Override
    public boolean checkTypeCodeExists(String typeCode, Long excludeId) {
        Long l = this.baseMapper.selectCount(new LambdaQueryWrapper<ErpFinanceVoucherType>()
            .eq(ErpFinanceVoucherType::getTypeCode, typeCode)
            .eq(null != excludeId, ErpFinanceVoucherType::getId, excludeId)
        );
        if(l > 0){
            return true;
        }
        return false;
    }

    /**
     * 校验凭证字是否存在
     * @param prefix
     * @param excludeId
     * @return
     */
    @Override
    public boolean checkPrefixExists(String prefix, Long excludeId) {
        Long l = this.baseMapper.selectCount(new LambdaQueryWrapper<ErpFinanceVoucherType>()
            .eq(ErpFinanceVoucherType::getPrefix, prefix)
            .eq(null != excludeId, ErpFinanceVoucherType::getId, excludeId)
        );
        if(l > 0){
            return true;
        }
        return false;
    }


    /**
     * 生成编码
     * @param voucherTypeId
     * @param period
     * @return
     */
    @Override
    public String generateVoucherNo(Long voucherTypeId, String period) {
        // 获取凭证类型信息
        ErpFinanceVoucherType voucherType = getById(voucherTypeId);
        if (voucherType == null) {
            throw new ServiceException("凭证类别不存在");
        }

        // 查询当前凭证类型在当前期间的最大序号
        Integer maxSerial = getMaxSerialNumber(voucherTypeId, period);

        // 计算新序号，首次生成则从1开始
        int newSerial = (maxSerial == null) ? 1 : maxSerial + 1;

        // 格式化序号为4位数字（0001, 0002...）
        String serialNumber = String.format("%04d", newSerial);

        // 拼接完整凭证号（例如：记-202508-0001，收-202508-0001）
        return voucherType.getPrefix() + "-" + period + "-" + serialNumber;
    }

    /**
     * 查询指定凭证类型和期间的最大序号
     * 不同类型（收、付、转等）和不同期间的序号完全独立
     */
    private Integer getMaxSerialNumber(Long voucherTypeId, String period) {
        return voucherTypeMapper.findMaxSerialByTypeAndPeriod(voucherTypeId, period);
    }

    /**
     * 转换为VO对象
     */
    private ErpFinanceVoucherTypeVo convertToVO(ErpFinanceVoucherType entity) {
        ErpFinanceVoucherTypeVo vo = new ErpFinanceVoucherTypeVo();
        BeanUtil.copyProperties(entity, vo);
        vo.setStatusName(entity.getStatus() == 1 ? "启用" : "禁用");
        return vo;
    }


    /**
     * 解析源凭证号，提取主凭证标识和子序号
     * @param sourceVoucherNo 源凭证号（如"记-2025-08-0001"或"记-2025-08-0001-001"）
     * @return 数组 [主凭证标识, 子序号]，子序号可能为null（主凭证）
     */
    public String[] parseSourceVoucherNo(String sourceVoucherNo) {
        String[] parts = sourceVoucherNo.split("-");
        if (parts.length < 4) {
            throw new ServiceException("源凭证号格式错误: " + sourceVoucherNo + "，正确格式应为[前缀-年-月-主序号]或[前缀-年-月-主序号-子序号]");
        }
        // 主凭证标识固定为前4段（如"记-2025-08-001"）
        String mainVoucherNo = String.join("-", parts[0], parts[1], parts[2], parts[3]);
        // 子序号（如果存在，第5段）
        String subNo = parts.length >= 5 ? parts[4] : null;
        return new String[]{mainVoucherNo, subNo};
    }

    /**
     * 计算新子序号（同一主凭证下递增）
     * @param mainVoucherNo 主凭证标识（如"记-2025-08-001"）
     * @return 新子序号（如1、2、3...）
     */
    public int calculateNewSubSerial(String mainVoucherNo) {
        // 查询该主凭证下已有的最大子序号（数据库层实现）
        Integer maxSubSerial = voucherTypeMapper.findMaxSubSerialByMainVoucherNo(mainVoucherNo);
        // 若没有子凭证，从1开始；否则+1
        return maxSubSerial == null ? 1 : maxSubSerial + 1;
    }
}
